#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <time.h>
#include <signal.h>
#include <sys/mman.h>

#include <unistd.h>

#include "proto.h"

#include <errno.h>

#define IPSTRSIZE 40
#define BUFSIZE 1024
#define PROCNUM 4
#define MINSPARESERVER 5
#define MAXSPARESERVER 5
#define MAXCLIENTS 30

#define SIG_NOTIFY SIGUSR2

enum
{
    STATE_IDEL = 0,
    STATE_BUSY
};

struct server_st
{
    pid_t pid;
    int state;
};

static int sd;

static struct server_st *serverpool;
static int idel_count = 0, busy_count = 0, server_count = 0;

static void usr2_hander(int s)
{
    return;
}

static int add_1_server(void);

static int del_1_server(void);

static void server_job(int pos);

static int scan_pool(void);

int main()
{
    struct sigaction sa, osa;
    sigset_t set, oset;

    sa.sa_handler = SIG_IGN;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = SA_NOCLDWAIT;
    sigaction(SIGCHLD, &sa, &osa);

    sa.sa_handler = usr2_hander;
    sigemptyset(&sa.sa_mask);
    sa.sa_flags = 0;
    sigaction(SIG_NOTIFY, &sa, &osa);

    sigemptyset(&set);
    sigemptyset(&oset);
    sigaddset(&set, SIG_NOTIFY);
    if (sigprocmask(SIG_BLOCK, &set, &oset) == -1)
    {
        perror("sigprocmask()");
        exit(1);
    }

    // 创建共享内存
    serverpool = mmap(NULL, sizeof(struct server_st) * MAXCLIENTS, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (serverpool == MAP_FAILED)
    {
        perror("mmap()");
        exit(1);
    }

    for (int i = 0; i < MAXCLIENTS; i++)
    {
        serverpool[i].pid = -1;
        serverpool[i].state = STATE_IDEL;
    }

    sd = socket(AF_INET, SOCK_STREAM, 0 /*IPPROTO_TCP, IPPROTO_SCTP*/);
    if (sd < 0)
    {
        perror("socket()");
        exit(1);
    }

    int val = 1;
    if (setsockopt(sd, SOL_SOCKET, SO_REUSEADDR, &val, sizeof(val)) < 0)
    {
        perror("setsockopt()");
        exit(1);
    }

    struct sockaddr_in laddr;
    laddr.sin_family = AF_INET;
    laddr.sin_port = htons(SERVERPORT);
    inet_pton(AF_INET, "0.0.0.0", &laddr.sin_addr);

    if (bind(sd, (struct sockaddr *)&laddr, sizeof(laddr)) < 0)
    {
        perror("bind()");
        exit(1);
    }

    if (listen(sd, 100) < 0)
    {
        perror("listen()");
        exit(1);
    }

    pid_t pid;
    for (int i = 0; i < MAXSPARESERVER; i++)
    {
        add_1_server();
    }

    while (1)
    {
        sigsuspend(&oset);
        scan_pool(); //遍历池

        if (idel_count > MAXSPARESERVER)
            for (int i = 0; i < idel_count - MAXSPARESERVER; i++)
                del_1_server();
        else if (idel_count < MINSPARESERVER && server_count < MAXCLIENTS)
            for (int i = 0; i < MINSPARESERVER - idel_count; i++)
                add_1_server();

        //printf the pool
        for (int i = 0; i < MAXCLIENTS; i++)
        {
            if (serverpool[i].pid == -1)
                putchar('_');
            else if (serverpool[i].state == STATE_IDEL)
                putchar('.');
            else
                putchar('x');
        }
        putchar('\n');
    }

    sigprocmask(SIG_SETMASK, &oset, NULL);
    close(sd);
    exit(0);
}

static int add_1_server(void)
{
    if (server_count >= MAXCLIENTS)
        return -1;

    int slot;

    for (slot = 0; slot < MAXCLIENTS; slot++)
        if (serverpool[slot].pid == -1)
            break;

    serverpool[slot].state = STATE_IDEL;
    pid_t pid = fork();
    if (pid < 0)
    {
        perror("fork()");
        exit(1);
    }

    if (pid == 0) //child
    {
        server_job(slot);
        exit(0);
    }
    else //parent
    {
        serverpool[slot].pid = pid;
        idel_count++;
        server_count++;
    }
    return 0;
}

static int del_1_server(void)
{
    if (idel_count == 0)
        return -1;

    for (int i = MAXCLIENTS - 1; i >= 0; i--)
    {
        if (serverpool[i].pid != -1 && serverpool[i].state == STATE_IDEL)
        {
            kill(serverpool[i].pid, SIGTERM);
            serverpool[i].pid = -1;
            idel_count--;
            server_count--;
            break;
        }
    }
    return 0;
}

static void server_job(int pos)
{
    struct sockaddr_in raddr;
    socklen_t raddr_len;
    pid_t ppid = getppid();
    while (1)
    {
        serverpool[pos].state = STATE_IDEL;
        kill(ppid, SIG_NOTIFY);

        int client_sd = accept(sd, (struct sockaddr *)&raddr, &raddr_len);
        if (client_sd < 0)
        {
            if (errno != EINTR || errno != EAGAIN)
            {
                perror("accept()");
                exit(1);
            }
        }

        serverpool[pos].state = STATE_BUSY;
        kill(ppid, SIG_NOTIFY);

        char linebuf[BUFSIZE];
        int len = snprintf(linebuf, BUFSIZE, FMT_STAMP, (long long)time(NULL));

        if (send(client_sd, linebuf, len, 0) < 0)
        {
            perror("send()");
            exit(1);
        }
        close(client_sd);
        sleep(5);
    }
}

static int scan_pool(void)
{
    int busy = 0, idle = 0;

    for (int i = 0; i < MAXCLIENTS; i++)
    {
        if (serverpool[i].pid == -1)
            continue;

        if (kill(serverpool[i].pid, 0))
        {
            serverpool[i].pid = -1;
            continue;
        }

        if (serverpool[i].state == STATE_IDEL)
            idle++;
        else if (serverpool[i].state == STATE_BUSY)
            busy++;
        else
        {
            fprintf(stderr, "Unknown state.\n");
            //_exit(1);
            abort();
        }
    }
    idel_count = idle;
    return 0;
}